package junior.db.dao.mybatis.mapper;

import junior.db.dao.base.IBaseId;
import junior.db.dao.base.IEntityTypeAssigned;
import junior.db.dao.mybatis.type.ResultType;
import junior.db.dao.query.AbstractQuery;
import junior.db.dao.query.QueryMapperProvider;
import junior.db.dao.update.BatchInsertOption;
import junior.db.dao.update.BatchUpdateOption;
import junior.db.dao.update.BatchUpsertOption;
import junior.db.dao.update.DeleteOption;
import junior.db.dao.update.InsertOption;
import junior.db.dao.update.UpdateFieldOption;
import junior.db.dao.update.UpdateMapperProvider;
import junior.db.dao.update.UpdateOption;
import junior.db.dao.update.UpsertOption;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author wubo
 * @date 2024/8/1 19:18
 */
public interface EntityMapper {
    
    /*--------- insert methods ----------*/
    
    /**
     * 批量插入
     * @param option
     * @param entity
     * @return
     */
    @InsertProvider(type = UpdateMapperProvider.class, method = "insert")
    @Options(useGeneratedKeys=true, keyColumn="id", keyProperty="entity.id")
    <E extends IBaseId> int insert(@Param("entity") E entity, @Param("option") InsertOption option);
    
    /**
     * <h3>批量更新</h3>
     * @param option
     * @param entities
     * @return 影响记录数
     */
    @InsertProvider(type=UpdateMapperProvider.class, method="batchInsert")
    @Options(useGeneratedKeys=true, keyColumn="id", keyProperty="entities.id")
    <E extends IBaseId> int batchInsert(@Param("entities") List<E> entities, @Param("option") BatchInsertOption option);
    
    /**
     * <h3>插入或更新，如果是自增主键，将会设置到entity.id</h3>
     * @param option
     * @param entity
     * @return 影响记录数
     */
    @InsertProvider(type=UpdateMapperProvider.class, method="upsert")
    @Options(useGeneratedKeys=true, keyColumn="id", keyProperty="entity.id")
    <E extends IBaseId> int upsert(@Param("entity") E entity, @Param("option") UpsertOption option);
    
    /**
     * <h3>批量插入或更新</h3>
     * 注意：此方法不能返回自增的主键
     * @param option
     * @param entities
     * @return 影响记录数
     */
    @InsertProvider(type=UpdateMapperProvider.class, method="batchUpsert")
    <E> int batchUpsert(@Param("entities") List<E> entities, @Param("option") BatchUpsertOption option);
    
    /*--------- update methods ----------*/
    
    @UpdateProvider(type = UpdateMapperProvider.class, method = "update")
    <E> int update(@Param("entity") E entity, @Param("option") UpdateOption option);
    
    @UpdateProvider(type = UpdateMapperProvider.class, method = "batchUpdate")
    <E> int batchUpdate(@Param("entities") List<E> entities, @Param("option") BatchUpdateOption option);
    
    @UpdateProvider(type = UpdateMapperProvider.class, method = "updateFieldWithEntityClass")
    <E> int updateField(@Param("entityClass") Class<E> entityClass, @Param("option") UpdateFieldOption option);
    
    /*--------- delete methods ----------*/
    
    @DeleteProvider(type = UpdateMapperProvider.class, method = "deleteWithEntityClass")
    <E> int delete(@Param("entityClass") Class<E> entityClass, @Param("option") DeleteOption option);
    
    /*--------- query methods ----------*/
    
    @SelectProvider(type = QueryMapperProvider.class, method = "count")
    <Q extends AbstractQuery & IEntityTypeAssigned> Long count(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <E, Q extends AbstractQuery & IEntityTypeAssigned> E get(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "getForObject")
    <T, Q extends AbstractQuery & IEntityTypeAssigned> T getForObject(@Param("query") Q query, @Param("resultType") ResultType<T> resultType);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> Map<String, Object> getForMap(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> String getForString(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> Integer getForInteger(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> Long getForLong(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> Boolean getForBoolean(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> BigDecimal getForBigDecimal(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> Date getForDate(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> LocalDate getForLocalDate(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> LocalDateTime getForLocalDateTime(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <E, Q extends AbstractQuery & IEntityTypeAssigned> List<E> list(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "getForObject")
    <T, Q extends AbstractQuery & IEntityTypeAssigned> List<T> listForObject(@Param("query") Q query, @Param("resultType") ResultType<T> resultType);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<Map<String, Object>> listForMap(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<String> listForString(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<Integer> listForInteger(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<Long> listForLong(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<Boolean> listForBoolean(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<BigDecimal> listForBigDecimal(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<Date> listForDate(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<LocalDate> listForLocalDate(Q query);
    
    @SelectProvider(type = QueryMapperProvider.class, method = "get")
    <Q extends AbstractQuery & IEntityTypeAssigned> List<LocalDateTime> listForLocalDateTime(Q query);
    
}